/*jslint vars: true, forin: true, plusplus: true */
/*global angular: false, engine: false, $: false */
engine.factory("Capture", function () {
	'use strict';
	/**
	 * editor single class
	 * @class editor
	 * @final
	 */
	var editor = {
		cssPath: "",
		curJQ: null
	};
	/**
	 * Capture the DOM after mouse click.
	 * @method catchHTML
	 * @returns {string} A only CSSpath.
	 */
	editor.catchHTML = function (callback) {
		// return a only CSSpath
		this.cssPath = this.getCSSpath();
		if (typeof callback === "function") {
			callback(this.cssPath);
		}
		return this.cssPath;
	};
	/**
	 * Watch each DOM when mouse over.
	 * @method watchHTML
	 */
	editor.watchHTML = function (callback) {
		var axes, axes_x, axes_y, markElement, catchElement, drawArea, eraseArea, scrollTop, scrollLeft,
			left = 1,
			top = 1,
			that = this;

		scrollTop = $(window[0].document).scrollTop();
		scrollLeft = $(window[0].document).scrollLeft();
		axes = '<div id="axes"><hr id="axes-x" /><hr id="axes-y" /><input id="control"/></div>';

		var iframent = window[0].document;
		markElement = function () {
			axes.hide();
			var current_element;
			axes_x.css("left", left + "px");
			axes_y.css("top", top + "px");
			$("#control").focus();
			current_element = $(iframent.elementFromPoint(left, top));
			if (that.curJQ !== current_element) {
				that.curJQ = current_element;
				eraseArea();
				drawArea();
			}
			axes.show();
		};

		catchElement = function () {
			axes.remove();
			that.catchHTML(callback);
		};

		drawArea = function () {
			var ele = that.curJQ;
			axes.append('<div class="area" style="' +
				'top:' + (ele.offset().top - scrollTop) +
				'px;left:' + (ele.offset().left - scrollLeft) +
				'px;width:' + ele.get(0).offsetWidth +
				'px;height:' + ele.get(0).offsetHeight +
				'px;"></div>');
		};

		eraseArea = function () {
			$(".area").remove();
		};
		$("body").append(axes);
		axes_x = $("#axes-x");
		axes_y = $("#axes-y");
		axes = $("#axes");

		axes.mousemove(function (event) {
			left = event.pageX;
			top = event.pageY;
			markElement();
		});

		$("#control").keydown(function (event) {
			if (event.which === 38) {
				top--;
			} else if (event.which === 40) {
				top++;
			} else if (event.which === 37) {
				left--;
			} else if (event.which === 39) {
				left++;
			} else if (event.which === 13) {
				catchElement();
			} else {
				return false;
			}
			markElement();
		});

		axes.click(function () {
			catchElement();
		});

	};
	/**
	 * Get the string of CSSpath.
	 * @method _getCSSpath
	 * @param [jq=curJQ] {jQuery} One DOM in the jQuery object.
	 * @param isShort {boolean}
	 * @returns {string} A only CSSpath.
	 * @private
	 * @beta
	 */
	editor.getCSSpath = function (jq, isShort) {
		var isOnly = false,
			orgin_jq,
			jq_element = jq,
			cssPath = "",
			deepi = 0,
			deepth = 6;
		// If no input jq param, jq is curJQ.
		if (!(jq instanceof $) || jq.length !== 1) {
			orgin_jq = this.curJQ;
			jq_element = this.curJQ;
		}
		// isShort is default "true" for shortest path.
		if (typeof (isShort) !== "boolean") {
			isShort = true;
		}
		// Compute the cssPath!
		cssPath = this.getCSSselector(jq_element);
		while (!(isOnly = this.isOnly(cssPath)) || !isShort) {
			// _jq become its parent.
			if ((jq_element = jq_element.parent()).get(0) instanceof window.HTMLBodyElement || ++deepi === deepth) {
				break;
			}
			cssPath = this.getCSSselector(jq_element) + " > " + cssPath;
		}
		if (!isOnly) {
			cssPath = cssPath + ":eq(" + $(cssPath, window[0].document).index(orgin_jq.get(0)) + ")";
		}
		return cssPath;
	};
	/**
	 * Get the CSS selector of DOM in string.
	 * @method getCSSselector
	 * @param [jq=curJQ] {jQuery} One DOM in the jQuery object.
	 * @param opts {object}
	 * @returns {string} The selector in string.
	 * @private
	 */
	editor.getCSSselector = function (jq, opts) {
		var domClass, domId, domAttr, attrName, cssSelector = "",
			options = {
				enableId: true,
				enableClass: true,
				enableAttr: true,
				attrs: {
					type: true,
					href: false,
					title: false,
					name: false
				}
			};
		//TODO emum key of otpions same in opts. Replace them.
		cssSelector = jq.prop("nodeName").toLowerCase();

		// "Class" selector.
		if (options.enableClass && (domClass = jq.attr("class"))) {
			cssSelector += "." + domClass.replace(/ /g, ".");
		}
		// "Id" selector.
		if (options.enableId && (domId = jq.attr("id"))) {
			cssSelector += "#" + domId;
		}
		// "Attributes" selector.
		if (options.enableAttr) {
			for (attrName in options.attrs) {
				if (options.attrs.hasOwnProperty(attrName) && options.attrs[attrName] && (domAttr = jq.attr(attrName))) {
					cssSelector += "[" + attrName + "=" + domAttr + "]";
				}
			}
		}

		return cssSelector;
	};

	/**
	 * Make sure the DOM can be selected only by CSSpath.
	 * @method _isOnly
	 * @param cssPath {string}
	 * @returns {boolean} True in length === 1.
	 * @private
	 */
	editor.isOnly = function (cssPath) {
		return $(cssPath, window[0].document).length === 1;
	};

	return editor;

});
